#include <iostream>
#include <fftw3.h>
#include <complex>
#include <queue>
#include <mutex>
#include "myfft.hpp"
#include "main.hpp"
using namespace std;

// * build and init
// * param: size_t size FFT's points
// * param: bool flag choose modes:IFFT or FFT
myfft::myfft(size_t _size = 512, bool _flag = true) : data_len(_size), flag(_flag)
{
    fftw_input = fftw_alloc_complex(data_len); // allocate memory
    fftw_output = fftw_alloc_complex(data_len);
    if (flag == true) // choose mode
    {
        plan = fftw_plan_dft_1d(data_len, fftw_input, fftw_output, FFTW_FORWARD, FFTW_ESTIMATE);
        cout << "fftw_plan is fft" << endl;
    }
    else
    {
        plan = fftw_plan_dft_1d(data_len, fftw_input, fftw_output, FFTW_BACKWARD, FFTW_ESTIMATE);
        cout << "fftw_plan is ifft" << endl;
    }
}

// * destroy
// * destroy plan, input and output's dymastic memeory
myfft::~myfft()
{

    fftw_free(fftw_input);
    fftw_free(fftw_output);
    fftw_destroy_plan(plan);
}

// * FFT/IFFt
// * param:complex<double>* data_input
void myfft::exec_fft(complex<double> *data_input, size_t data_size)
{
    // switch queue<complex<double>> into fftw_complex
    if (data_size < data_len) // size < data_len, add 0 at the end
    {
        for (size_t i = 0; i < data_size; i++)
        {
            fftw_input[i][0] = data_input[i].real();
            fftw_input[i][1] = data_input[i].imag();
        }
        for (size_t i = data_size; i < data_len; i++)
        {
            fftw_input[i][0] = 0;
            fftw_input[i][1] = 0;
        }
    }
    else if (data_size == data_len)
    {
        for (size_t i = 0; i < data_size; i++)
        {
            fftw_input[i][0] = data_input[i].real();
            fftw_input[i][1] = data_input[i].imag();
        }
    }
    else
    {
        cerr << "input queue size does not match FFT size." << endl; // console error
    }

    fftw_execute(plan); // execute task
}

// * output result
// * output switch fftw_complex into queue<complex<double>>
// ! remind to release memory in the main function
short *myfft::get_result()
{
    short *pre_date_time = new short[FFT_SIZE * 2];
    for (size_t i = 0; i < data_len; i++)
    {
        // pre_date_time.push(static_cast<short>(fftw_output[i][0]));
        // pre_date_time.push(static_cast<short>(fftw_output[i][1]));
        pre_date_time[2 * i] = static_cast<short>(fftw_output[i][0]);
        pre_date_time[2 * i + 1] = static_cast<short>(fftw_output[i][1]);
    }
    return pre_date_time;
}